//
//  VPaiDiscovery.swift
//  VPai
//
//  Created by devel on 16/9/13.
//  Copyright © 2016年 viatech. All rights reserved.
//

import Foundation
import UIKit
import SwiftyJSON
import Async
import tcp2usb


let ReachabilityStatusChangedNotification = "ReachabilityStatusChangedNotification"

/**
 一个发现远程设备的类，自动监听组广播已发现新的设备
 */
class VPaiDiscovery: NSObject,  DeviceListener {
    
    private static let TAG = "VPaiDiscovery"
    private static var sDiscover : VPaiDiscovery?
    
    //单例模式
    class var shareInstance : VPaiDiscovery{
        struct Static {
            static var instance: VPaiDiscovery?
            static var token: dispatch_once_t = 0
        }
        
        dispatch_once(&Static.token){
            Static.instance = VPaiDiscovery()
        }
        
        return Static.instance!
    }
    
    private let VPAI_TOKEN = "VPAI_TOKEN";
    private let MSG_CLIENT_SNOOP = "{\"token\" : \"VPAI_TOKEN\"}";
    private let broadcast_port:UInt16 = 8721
    private let multicast_address:String = "224.0.1.1"
    private let singlecast_address:String = "192.168.5.1"
    private let checkTime : Double = 10 //检查设备有效的时间间隔
    private let lastInterval : Double = 60 //当超过这个时间还没收到设备的广播，则认为这个设备已不存在
    //private let reach = Reach()
    private let listenerList = ListenerList()
    private var checktimer : NSTimer?
    private var resendTimer : NSTimer?
    private var stopped = true
    private var socket_udp : AsyncUdpSocket?
    private var devices  : [VPaiDevice] = []
    
    private override init() {
        super.init()
        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(VPaiDiscovery.networkStatusChanged(_:)), name: ReachabilityStatusChangedNotification, object: nil)
       // reach.monitorReachabilityChanges()
        startDiscover(true)
        
        TCP2USB.createHostSide(self)
        
        checktimer = NSTimer.scheduledTimerWithTimeInterval(checkTime, target: self, selector: #selector(VPaiDiscovery.checkDeviceValid(_:)), userInfo: nil, repeats: true)
    }
    
    deinit{
        //Log.info(VPaiDiscovery.TAG, msg: "deinit()")
    }
    
    func networkStatusChanged(notification: NSNotification) {
       // Log.info(VPaiDiscovery.TAG, msg: "networkStatusChanged")
        startDiscover(true)
    }
    
    func registerListener(listener : VPaiDiscoveryListener) {
        listenerList.registerListener(listener)
    }
    
    func unregisterListener(listener : VPaiDiscoveryListener) {
        listenerList.unregisterListener(listener)
    }
    
    func startDiscover(recreate : Bool){
        stopped = false
        var newUDP = false
        
        if recreate || socket_udp == nil {
            socket_udp?.close()
            socket_udp = nil
            
           // socket_udp = AsyncUdpSocket(IPv4:())
            socket_udp?.setDelegate(self)
            do{
                try socket_udp?.bindToPort(broadcast_port)
                try socket_udp?.enableBroadcast(true)
                try socket_udp?.joinMulticastGroup(multicast_address)
            }catch{
             //   Log.error(VPaiDiscovery.TAG, msg: "enableBroadcast or joinMulticastGroup error")
                socket_udp?.close()
                socket_udp = nil
            }
            newUDP = true
        }
        
        socket_udp?.receiveWithTimeout(-1, tag:0)
        sendRequest()
        resendTimer?.invalidate()
        resendTimer = nil
        resendTimer = NSTimer.scheduledTimerWithTimeInterval(2, target: self, selector: #selector(VPaiDiscovery.resend(_:)), userInfo: nil, repeats: false)
      //  Log.info(VPaiDiscovery.TAG, msg: "startdiscovery: newUDP=\(newUDP),socket=\(socket_udp)")
    }
    
    func resend(t: NSTimer) {
        resendTimer?.invalidate()
        resendTimer = nil
        sendRequest()
    }
    
    private func sendRequest(){
        socket_udp?.sendData(MSG_CLIENT_SNOOP.dataUsingEncoding(NSUTF8StringEncoding), toHost: "255.255.255.255", port: broadcast_port, withTimeout: 10, tag: 1)
        socket_udp?.sendData(MSG_CLIENT_SNOOP.dataUsingEncoding(NSUTF8StringEncoding), toHost: multicast_address, port: broadcast_port, withTimeout: 10, tag: 1)
        socket_udp?.sendData(MSG_CLIENT_SNOOP.dataUsingEncoding(NSUTF8StringEncoding), toHost: singlecast_address, port: broadcast_port, withTimeout: 10, tag: 1)
    }
    
    func onUdpSocket(sock: NSString, didReceiveData data: NSData!, withTag tag: Int, fromHost host: String!, port: UInt16) -> Bool {
        if !stopped {
            socket_udp?.receiveWithTimeout(-1, tag:0)
        }
        let msg = NSString(data:data, encoding:NSUTF8StringEncoding)!
      //  Log.verbose(VPaiDiscovery.TAG, msg: "msg = \(msg)")
        
        let json = SwiftyJSON.JSON.parse(msg as String)
        let token = json["token"].string ?? ""
        if token == VPAI_TOKEN {
            let deviceid = json["deviceid"].string ?? ""
            var device = findDeviceByID(deviceid)
            var new = false
            if device == nil {
                device = VPaiDevice()
                new = true
            }
            device!.name = json["name"].string ?? ""
            device!.deviceid = json["deviceid"].string ?? ""
            device!.version = json["version"].string ?? ""
            device!.token = json["token"].string ?? ""
            device!.mediaport = json["mediaport"].int ?? 0
            device!.ctrlport = json["ctrlport"].int ?? 0
            device!.ipAddr = host
            device!.lastActiveTime = NSDate().timeIntervalSince1970
            
            //we can get {"token" : "VPAI_TOKEN"} ,so check it
            if device!.name.isEmpty || device!.deviceid.isEmpty || device!.version.isEmpty {
                return true
            }
            
            if new {
                dispatch_async(dispatch_get_main_queue(), {
                   // Log.info(VPaiDiscovery.TAG, msg: "find new device, notify all:\(device!.toString())")
                    self.devices.append(device!)
                    self.listenerList.foreach({ (l : VPaiDiscoveryListener, index) in
                        l.onDevicesChange(self.devices)
                    })
                })
            }
        }
        
        return true
    }
    
    func onUSBDeviceStatus(final snoopString:String, final status:Int){
        
      //  Log.info(VPaiDiscovery.TAG, "onUSBDeviceStatus snoop: \(snoopString) status:\(status)")
        if status == UsbTransfer.Status_Connected {
            //HAMLogOutputWindow.printLog("onUSBDeviceStatus status==Status_Connected")
            
            dispatch_async(dispatch_get_main_queue(), {
                let url = TCP2USB.getForwardAddress("127.0.0.1", port: "8721")
                let url2 = TCP2USB.getForwardAddress("127.0.0.1", port: "8720")
                
                let device = VPaiDevice()
                let splitedarray = url.characters.split {$0 == ":"}
                device.ipAddr = String(splitedarray[0])
                var port:Int = 0
                if let number = Int(String(splitedarray[1])){
                    port = number
                }
                
                let splitedarray2 = url2.characters.split {$0 == ":"}
                //device.ipAddr = String(splitedarray2[0])
                var mediaport:Int = 0
                if let number = Int(String(splitedarray2[1])){
                    mediaport = number
                }
                
                device.ctrlport = port
                device.mediaport = mediaport
                device.type = "usb"
                
                device.name = "smalleye"
                
        //        Log.info(VPaiDiscovery.TAG, msg: "find new usb device, notify all:\(device.toString())")

                self.listenerList.foreach({ (l : VPaiDiscoveryListener, index) in
                    l.onDevicesChange(self.devices)
                })
                self.devices.removeAll()
                
                self.devices.append(device)
                
                //HAMLogOutputWindow.printLog("onUSBDeviceStatus count:\(self.devices.count)")

                self.listenerList.foreach({ (l : VPaiDiscoveryListener, index) in
                    l.onDevicesChange(self.devices)
                })
            })
        }else if status == UsbTransfer.Status_Disconnected {
            dispatch_async(dispatch_get_main_queue(), {
         //       Log.debug(VPaiDiscovery.TAG, "onUSBDeviceStatus status==Status_Disconnected")
                //HAMLogOutputWindow.printLog("onUSBDeviceStatus status==Status_Disconnected")

                for device in self.devices {
                    //Log.verbose(VPaiDiscovery.TAG, msg: device.toString())
                    if device.type == "usb"{
                        let index = self.devices.indexOf(device)
                        //HAMLogOutputWindow.printLog("onUSBDeviceStatus remove index:\(index)")
                        if index != nil {
                            self.devices.removeAtIndex(index!)
                        }
                        break
                    }
                }
                //HAMLogOutputWindow.printLog("onUSBDeviceStatus count:\(self.devices.count)")

                self.listenerList.foreach({ (l : VPaiDiscoveryListener, index) in
                    l.onDevicesChange(self.devices)
                })
            })
        }
    }
    
    func onCustomCommand(cmd:UInt8, payload: [UInt8]){
        
    }

    func checkDeviceValid(t: NSTimer) {
        let now = NSDate().timeIntervalSince1970
        //Log.verbose(VPaiDiscovery.TAG, msg: "checkDeviceValid : now = \(now)")
        var list : [VPaiDevice] = []
        var change = false
        for device in devices {
            //Log.verbose(VPaiDiscovery.TAG, msg: device.toString())
            if device.type != "usb" && now - device.lastActiveTime > lastInterval {
                change = true
            }else {
                list.append(device)
            }
        }
        
        if change {
            dispatch_async(dispatch_get_main_queue(), {
             //   Log.info(VPaiDiscovery.TAG, msg: "devices change, notify all")
                self.devices = list
                self.listenerList.foreach({ (l : VPaiDiscoveryListener, index) in
                    l.onDevicesChange(self.devices)
                })
            })
            
        }
    }
    
    private func findDeviceByID(deviceid : String) -> VPaiDevice? {
        for device in devices {
            if device.deviceid == deviceid{
                return device
            }
        }
        return nil
    }
}

protocol VPaiDiscoveryListener : NSObjectProtocol {
    func onDevicesChange(devices : [VPaiDevice])
}

